/*
 * jPOS Project [http://jpos.org]
 * Copyright (C) 2000-2015 Alejandro P. Revilla
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.jpos.iso.gui;

import org.jpos.iso.ISOMsg;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

/**
 * ISOMsgPanel
 * Swing based GUI to ISOMsg
 *
 * @author apr@cs.com.uy
 * @author Kris Leite <kleite at imcsoftware.com>
 * @see org.jpos.iso.ISOMsg
 */
@SuppressWarnings("deprecation")
public class ISOMeter extends JComponent implements Runnable {

	private static final long serialVersionUID = -1770533267122111538L;
	/**
	 * @serial
	 */
	Color color = new Color(255, 255, 255);
	/**
	 * @serial
	 */
	Image im;
	/**
	 * @serial
	 */
	Graphics img;
	/**
	 * @serial
	 */
	Font fontBig, fontSmall;
	/**
	 * @serial
	 */
	String positiveText;
	/**
	 * @serial
	 */
	String negativeText;
	/**
	 * @serial
	 */
	Timer ti;
	/**
	 * handle ISOMeter's counters outside of this class in order
	 * to reduce 'int' to 'String' conversions.
	 *
	 * @serial
	 */
	String positiveCounter;
	/**
	 * @serial
	 */
	String negativeCounter;
	/**
	 * @serial
	 */
	int lastPositive;
	/**
	 * @serial
	 */
	int lastNegative;
	/**
	 * @serial
	 */
	boolean connected;
	/**
	 * @serial
	 */
	ISOChannelPanel parent;

	final static int width = 200;
	final static int height = 60;
	final static int mass = height / 2;
	final static int MAX_VALUE = 1000;

	/**
	 * @serial
	 */
	int[] yPoints;
	/**
	 * @serial
	 */
	int[] xPoints;
	/**
	 * counter to keep the scrolling active
	 */
	int continueScroll;
	/**
	 * used to determine if to scroll mark to end of graph
	 */
	boolean scroll = true;
	/**
	 * Refresh panel in millseconds
	 */
	int refreshPanel = 50;

	private Image imb;
	private Thread repaintThread;

	public ISOMeter(ISOChannelPanel parent) {
		super();
		this.parent = parent;

		fontBig = new Font("Helvetica", Font.ITALIC, mass * 3 / 4);
		fontSmall = new Font("Helvetica", Font.PLAIN, 10);
		yPoints = new int[width];
		xPoints = new int[width];
		for (int i = 0; i < width; i++) {
			xPoints[i] = i;
			yPoints[i] = mass;
		}
		positiveText = null;
		negativeText = null;
		positiveCounter = negativeCounter = "";
		connected = false;

		MouseListener mouseListener = new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				showLogList();
			}
		};
		addMouseListener(mouseListener);
	}

	public synchronized void start() {
		if (repaintThread == null) {
			repaintThread = new Thread(this, "ISOMeter");
			repaintThread.setPriority(Thread.NORM_PRIORITY - 1);
			repaintThread.start();
		}
	}

	public void showLogList() {
		JFrame f = new JFrame(parent.getSymbolicName());
		f.getContentPane().add(createLogList());
		f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		f.validate();
		f.pack();
		f.setSize(width, width + 50);
		f.show();
	}

	public JComponent createLogList() {
		final JList logList = new JList(parent.getLog());
		JPanel A = new JPanel();
		A.setLayout(new BorderLayout());

		MouseListener mouseListener = new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				ISOMsg m = (ISOMsg) logList.getSelectedValue();
				if (m != null) {
					JFrame f = new JFrame(
							parent.getSymbolicName() + ":" + m.toString());
					ISOMsgPanel p = new ISOMsgPanel(m);
					f.getContentPane().add(p);
					f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
					f.pack();
					f.show();
				}
			}
		};
		logList.addMouseListener(mouseListener);

		logList.setPrototypeCellValue("9999 99999999 999999");
		JScrollPane scrollPane = new JScrollPane(logList);
		A.add(scrollPane, BorderLayout.CENTER);
		return A;
	}

	public void setValue(int val) {
		int y = mass - val % 1000 * height / 2000;
		yPoints[width - 1] = y;
		continueScroll = width;
		scroll();
	}

	public void setScroll(boolean scroll) {
		this.scroll = scroll;
	}

	public void setRefresh(int refreshPanel) {
		if (refreshPanel > 0)
			this.refreshPanel = refreshPanel;
	}

	public void setConnected(boolean connected) {
		if (this.connected != connected) {
			if (!scroll)
				if (connected)
					continueScroll = width;
				else
					continueScroll = 1;
			repaint();
		}
		this.connected = connected;
	}

	public void setPositiveCounter(String s) {
		positiveCounter = s;
	}

	public void setNegativeCounter(String s) {
		negativeCounter = s;
	}

	public void setValue(int val, String textString) {
		setValue(val);
		if (val < 0) {
			negativeText = textString;
			lastNegative = 0;
		} else {
			positiveText = textString;
			lastPositive = 0;
		}
	}

	public void paint(Graphics g) {
		if (repaintThread == null)
			start();
		plot();
		g.drawImage(im, 0, 0, null);
	}

	public Dimension getPreferredSize() {
		return new Dimension(width, height);
	}

	private void scroll() {
		System.arraycopy(yPoints, 1, yPoints, 0, width - 1);
		if (continueScroll > 0)
			continueScroll--;
	}

	public void plot() {
		if (im == null) {
			im = createImage(width, height);
			img = im.getGraphics();
			img.setColor(Color.black);
			img.fillRoundRect(0, 0, width, height, 10, 10);
			img.clipRect(0, 0, width, height);
			plotGrid();

            /* save a copy of the image */
			imb = createImage(width, height);
			Graphics imbCopy = imb.getGraphics();
			imbCopy.drawImage(im, 0, 0, this);
		}
		img.drawImage(imb, 0, 0, this);
		if (continueScroll > 0)
			scroll();
		plotText(positiveText, lastPositive++, 3, mass - 3);
		plotText(negativeText, lastNegative++, 3, height - 3);
		plotCounters(positiveCounter, negativeCounter);
		img.setColor(connected ? Color.green : Color.red);
		img.drawPolyline(xPoints, yPoints, width);
	}

	private void plotGrid() {
		img.setColor(Color.blue);
		for (int i = 0; i < width; i++)
			if (i % 20 == 0)
				img.drawLine(i, 0, i, height);
		for (int i = -1000; i < 1000; i += 200) {
			int y = mass + i * height / 2000;
			img.drawLine(0, y, width, y);
		}
	}

	private void plotText(String t, int l, int x, int y) {
		if (t != null && l < 20) {
			img.setColor(Color.lightGray);
			img.setFont(fontBig);
			img.drawString(t, x, y);
		}
	}

	private void plotCounters(String p, String n) {
		img.setColor(Color.lightGray);
		img.setFont(fontSmall);
		img.drawString(p, width - 55, 13);
		img.drawString(n, width - 55, height - 3);
	}

	public void run() {
		while (isShowing()) {
			if (continueScroll > 0)
				repaint();
			try {
				Thread.sleep(refreshPanel);
			} catch (InterruptedException e) {
				// OK to ignore
			}
		}
		repaintThread = null;
	}

	public void update(Graphics g) {
		paint(g);
	}
}
